{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "voluntary-signal",
   "metadata": {},
   "source": [
    "## Description:\n",
    "这里是DIEN的一个demo， 主要分为数据读取与处理，模型搭建，模型的训练三大模块"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "metric-prediction",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:25.960198Z",
     "start_time": "2021-03-06T14:27:22.079518Z"
    }
   },
   "outputs": [],
   "source": [
    "# python基础包\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from collections import namedtuple\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 特征处理与数据集划分\n",
    "from sklearn.preprocessing import OneHotEncoder, MinMaxScaler, StandardScaler, LabelEncoder\n",
    "from sklearn.model_selection import train_test_split\n",
    "from utils import DenseFeat, SparseFeat, VarLenSparseFeat\n",
    "\n",
    "# 导入模型\n",
    "from DIEN import DIEN\n",
    "\n",
    "# 模型训练相关\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras.layers import *\n",
    "from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n",
    "from tensorflow.keras.metrics import AUC\n",
    "from tensorflow.keras.losses import binary_crossentropy\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "\n",
    "# 一些相关设置\n",
    "import warnings\n",
    "plt.style.use('fivethirtyeight')\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "better-vector",
   "metadata": {},
   "source": [
    "## 导入数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "hundred-rapid",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:26.825982Z",
     "start_time": "2021-03-06T14:27:26.808988Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"读取数据\"\"\"\n",
    "samples_data = pd.read_csv(\"data/movie_sample.txt\", sep=\"\\t\", header = None)\n",
    "samples_data.columns = [\"user_id\", \"gender\", \"age\", \"hist_movie_id\", \"hist_len\", \"movie_id\", \"movie_type_id\", \"label\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "trained-gibson",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:28.089319Z",
     "start_time": "2021-03-06T14:27:28.050423Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"数据集\"\"\"\n",
    "X = samples_data[[\"user_id\", \"gender\", \"age\", \"hist_movie_id\", \"hist_len\", \"movie_id\", \"movie_type_id\"]]\n",
    "y = samples_data[\"label\"]\n",
    "\n",
    "# 这里设置个负采样数据， 目前是直接等于了hist_movie_id\n",
    "X['neg_hist_movie_id'] = X['hist_movie_id']\n",
    "behavior_len = np.array([len([int(i) for i in l.split(',') if int(i) != 0]) for l in X['hist_movie_id']])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "forward-oriental",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:29.446622Z",
     "start_time": "2021-03-06T14:27:29.398723Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"构建DIEN模型的输入格式\"\"\"\n",
    "# 这里和DIN相比， 会多出负采样的一列历史行为\n",
    "\n",
    "X_train = {\"user_id\": np.array(X[\"user_id\"]), \\\n",
    "        \"gender\": np.array(X[\"gender\"]), \\\n",
    "        \"age\": np.array(X[\"age\"]), \\\n",
    "        \"hist_movie_id\": np.array([[int(i) for i in l.split(',')] for l in X[\"hist_movie_id\"]]), \\\n",
    "        \"neg_hist_movie_id\": np.array([[int(i) for i in l.split(',')] for l in X[\"neg_hist_movie_id\"]]), \\\n",
    "        \"seq_length\": behavior_len, \\\n",
    "        \"hist_len\": np.array(X[\"hist_len\"]), \\\n",
    "        \"movie_id\": np.array(X[\"movie_id\"]), \\\n",
    "        \"movie_type_id\": np.array(X[\"movie_type_id\"])}\n",
    "\n",
    "y_train = np.array(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "touched-headquarters",
   "metadata": {},
   "source": [
    "## 模型建立"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "accompanied-camera",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:31.362531Z",
     "start_time": "2021-03-06T14:27:31.356504Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"特征封装\"\"\"\n",
    "\n",
    "feature_columns = [SparseFeat('user_id', max(samples_data[\"user_id\"])+1, embedding_dim=8), \n",
    "                    SparseFeat('gender', max(samples_data[\"gender\"])+1, embedding_dim=8), \n",
    "                    SparseFeat('age', max(samples_data[\"age\"])+1, embedding_dim=8), \n",
    "                    SparseFeat('movie_id', max(samples_data[\"movie_id\"])+1, embedding_dim=8),\n",
    "                    SparseFeat('movie_type_id', max(samples_data[\"movie_type_id\"])+1, embedding_dim=8),\n",
    "                    DenseFeat('hist_len', 1)]\n",
    "feature_columns += [VarLenSparseFeat(SparseFeat('hist_movie_id', \n",
    "                                                vocabulary_size=max(samples_data[\"movie_id\"])+1,\n",
    "                                                embedding_dim=8,\n",
    "                                                embedding_name='item_id'), maxlen=50, length_name='seq_length')]\n",
    "feature_columns += [VarLenSparseFeat(SparseFeat('neg_hist_movie_id', \n",
    "                                                vocabulary_size=max(samples_data[\"movie_id\"])+1,\n",
    "                                                embedding_dim=8,\n",
    "                                                embedding_name='item_id'), maxlen=50, length_name='seq_length')]\n",
    "\n",
    "# 行为特征列表，表示的是基础特征\n",
    "behavior_feature_list = ['movie_id']\n",
    "# 行为序列特征\n",
    "behavior_seq_feature_list = ['hist_movie_id']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "stylish-savannah",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:32.581571Z",
     "start_time": "2021-03-06T14:27:32.571598Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"设置超参数\"\"\"\n",
    "learning_rate = 0.001\n",
    "batch_size = 64\n",
    "epochs = 50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "suited-application",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:39.734554Z",
     "start_time": "2021-03-06T14:27:34.232009Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From E:\\Jupyter Notebook\\GitHubRepositories\\AI-RecommenderSystem\\DIEN\\contrib\\rnn_v2.py:1049: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use `tf.cast` instead.\n",
      "WARNING:tensorflow:From c:\\users\\zhongqiangwu\\anaconda3\\envs\\tf2\\lib\\site-packages\\tensorflow\\python\\keras\\layers\\legacy_rnn\\rnn_cell_impl.py:577: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Call initializer instance with the dtype argument instead of passing it to the constructor\n",
      "WARNING:tensorflow:From c:\\users\\zhongqiangwu\\anaconda3\\envs\\tf2\\lib\\site-packages\\tensorflow\\python\\keras\\layers\\legacy_rnn\\rnn_cell_impl.py:587: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Call initializer instance with the dtype argument instead of passing it to the constructor\n",
      "WARNING:tensorflow:From E:\\Jupyter Notebook\\GitHubRepositories\\AI-RecommenderSystem\\DIEN\\DIEN.py:376: The name tf.keras.backend.get_session is deprecated. Please use tf.compat.v1.keras.backend.get_session instead.\n",
      "\n",
      "Model: \"model\"\n",
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "hist_movie_id (InputLayer)      [(None, 50)]         0                                            \n",
      "__________________________________________________________________________________________________\n",
      "movie_id (InputLayer)           [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "emb_hist_movie_id (Embedding)   (None, 50, 8)        1680        hist_movie_id[0][0]              \n",
      "__________________________________________________________________________________________________\n",
      "seq_length (InputLayer)         [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "user_id (InputLayer)            [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "gender (InputLayer)             [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "age (InputLayer)                [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "emb_movie_id (Embedding)        (None, 1, 8)         1672        movie_id[0][0]                   \n",
      "                                                                 movie_id[0][0]                   \n",
      "__________________________________________________________________________________________________\n",
      "movie_type_id (InputLayer)      [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "dynamic_gru (DynamicGRU)        (None, 50, 8)        408         emb_hist_movie_id[0][0]          \n",
      "                                                                 seq_length[0][0]                 \n",
      "__________________________________________________________________________________________________\n",
      "emb_user_id (Embedding)         (None, 1, 8)         32          user_id[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "emb_gender (Embedding)          (None, 1, 8)         24          gender[0][0]                     \n",
      "__________________________________________________________________________________________________\n",
      "emb_age (Embedding)             (None, 1, 8)         32          age[0][0]                        \n",
      "__________________________________________________________________________________________________\n",
      "emb_movie_type_id (Embedding)   (None, 1, 8)         80          movie_type_id[0][0]              \n",
      "__________________________________________________________________________________________________\n",
      "attention_pooling_layer (Attent (None, 1, 50)        72065       emb_movie_id[1][0]               \n",
      "                                                                 dynamic_gru[0][0]                \n",
      "__________________________________________________________________________________________________\n",
      "flatten (Flatten)               (None, 8)            0           emb_user_id[0][0]                \n",
      "__________________________________________________________________________________________________\n",
      "flatten_1 (Flatten)             (None, 8)            0           emb_gender[0][0]                 \n",
      "__________________________________________________________________________________________________\n",
      "flatten_2 (Flatten)             (None, 8)            0           emb_age[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "flatten_3 (Flatten)             (None, 8)            0           emb_movie_id[0][0]               \n",
      "__________________________________________________________________________________________________\n",
      "flatten_4 (Flatten)             (None, 8)            0           emb_movie_type_id[0][0]          \n",
      "__________________________________________________________________________________________________\n",
      "permute (Permute)               (None, 50, 1)        0           attention_pooling_layer[0][0]    \n",
      "__________________________________________________________________________________________________\n",
      "hist_len (InputLayer)           [(None, 1)]          0                                            \n",
      "__________________________________________________________________________________________________\n",
      "concatenate (Concatenate)       (None, 40)           0           flatten[0][0]                    \n",
      "                                                                 flatten_1[0][0]                  \n",
      "                                                                 flatten_2[0][0]                  \n",
      "                                                                 flatten_3[0][0]                  \n",
      "                                                                 flatten_4[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "dynamic_gru_1 (DynamicGRU)      (None, 8)            0           dynamic_gru[0][0]                \n",
      "                                                                 seq_length[0][0]                 \n",
      "                                                                 permute[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "concatenate_1 (Concatenate)     (None, 49)           0           hist_len[0][0]                   \n",
      "                                                                 concatenate[0][0]                \n",
      "                                                                 dynamic_gru_1[0][0]              \n",
      "__________________________________________________________________________________________________\n",
      "dense_8 (Dense)                 (None, 200)          10200       concatenate_1[0][0]              \n",
      "__________________________________________________________________________________________________\n",
      "dense_9 (Dense)                 (None, 80)           16160       dense_8[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "neg_hist_movie_id (InputLayer)  [(None, 50)]         0                                            \n",
      "__________________________________________________________________________________________________\n",
      "dense_10 (Dense)                (None, 1)            81          dense_9[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "emb_neg_hist_movie_id (Embeddin (None, 50, 8)        1680        neg_hist_movie_id[0][0]          \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice (Tens [(None, 49, 8)]      0           dynamic_gru[0][0]                \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_1 (Te [(None, 49, 8)]      0           emb_hist_movie_id[0][0]          \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_2 (Te [(None, 49, 8)]      0           emb_neg_hist_movie_id[0][0]      \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_concat (TensorFlowO [(None, 49, 16)]     0           tf_op_layer_strided_slice[0][0]  \n",
      "                                                                 tf_op_layer_strided_slice_1[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_concat_1 (TensorFlo [(None, 49, 16)]     0           tf_op_layer_strided_slice[0][0]  \n",
      "                                                                 tf_op_layer_strided_slice_2[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "dnn (DNN)                       (None, 49, 1)        6750        tf_op_layer_concat[0][0]         \n",
      "                                                                 tf_op_layer_concat_1[0][0]       \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Sub (TensorFlowOpLa [(None, 1)]          0           seq_length[0][0]                 \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Shape (TensorFlowOp [(3,)]               0           tf_op_layer_strided_slice_1[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_SequenceMask/Expand [(None, 1, 1)]       0           tf_op_layer_Sub[0][0]            \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_5 (Te [(None, 49)]         0           dnn[1][0]                        \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Shape_1 (TensorFlow [(3,)]               0           tf_op_layer_strided_slice_2[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_4 (Te [(None, 49)]         0           dnn[0][0]                        \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_6 (Te [()]                 0           tf_op_layer_Shape[0][0]          \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_SequenceMask/Cast ( [(None, 1, 1)]       0           tf_op_layer_SequenceMask/ExpandDi\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_sub_1 (TensorFlowOp [(None, 49)]         0           tf_op_layer_strided_slice_5[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_7 (Te [()]                 0           tf_op_layer_Shape_1[0][0]        \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Log (TensorFlowOpLa [(None, 49)]         0           tf_op_layer_strided_slice_4[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Reshape/shape (Tens [(2,)]               0           tf_op_layer_strided_slice_6[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_SequenceMask/Less ( [(None, 1, 49)]      0           tf_op_layer_SequenceMask/Cast[0][\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Log_1 (TensorFlowOp [(None, 49)]         0           tf_op_layer_sub_1[0][0]          \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Reshape_1/shape (Te [(2,)]               0           tf_op_layer_strided_slice_7[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Reshape (TensorFlow [(None, None)]       0           tf_op_layer_Log[0][0]            \n",
      "                                                                 tf_op_layer_Reshape/shape[0][0]  \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_strided_slice_3 (Te [(None, 49)]         0           tf_op_layer_SequenceMask/Less[0][\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Reshape_1 (TensorFl [(None, None)]       0           tf_op_layer_Log_1[0][0]          \n",
      "                                                                 tf_op_layer_Reshape_1/shape[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Neg (TensorFlowOpLa [(None, None)]       0           tf_op_layer_Reshape[0][0]        \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Cast (TensorFlowOpL [(None, 49)]         0           tf_op_layer_strided_slice_3[0][0]\n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Neg_1 (TensorFlowOp [(None, None)]       0           tf_op_layer_Reshape_1[0][0]      \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_mul (TensorFlowOpLa [(None, 49)]         0           tf_op_layer_Neg[0][0]            \n",
      "                                                                 tf_op_layer_Cast[0][0]           \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_mul_1 (TensorFlowOp [(None, 49)]         0           tf_op_layer_Neg_1[0][0]          \n",
      "                                                                 tf_op_layer_Cast[0][0]           \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_add (TensorFlowOpLa [(None, 49)]         0           tf_op_layer_mul[0][0]            \n",
      "                                                                 tf_op_layer_mul_1[0][0]          \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_Mean (TensorFlowOpL [()]                 0           tf_op_layer_add[0][0]            \n",
      "__________________________________________________________________________________________________\n",
      "tf_op_layer_mul_2 (TensorFlowOp [()]                 0           tf_op_layer_Mean[0][0]           \n",
      "__________________________________________________________________________________________________\n",
      "add_loss (AddLoss)              ()                   0           tf_op_layer_mul_2[0][0]          \n",
      "==================================================================================================\n",
      "Total params: 110,864\n",
      "Trainable params: 110,864\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "\"\"\"构建DIN模型\"\"\"\n",
    "model = DIEN(feature_columns, behavior_feature_list, behavior_seq_feature_list, use_neg_sample=True)\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "former-identifier",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:42.544744Z",
     "start_time": "2021-03-06T14:27:42.412807Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"模型编译\"\"\"\n",
    "model.compile(loss=binary_crossentropy, optimizer=Adam(learning_rate=learning_rate), metrics=[AUC()])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "identified-scope",
   "metadata": {},
   "source": [
    "## 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "impressive-renewal",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:58.298571Z",
     "start_time": "2021-03-06T14:27:45.651308Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 1104 samples, validate on 276 samples\n",
      "Epoch 1/50\n",
      "1104/1104 [==============================] - 2s 2ms/sample - loss: 1.5078 - auc: 0.4850 - val_loss: 0.8970 - val_auc: 0.4973\n",
      "Epoch 2/50\n",
      "1104/1104 [==============================] - 1s 692us/sample - loss: 0.9351 - auc: 0.4893 - val_loss: 0.7694 - val_auc: 0.4897\n",
      "Epoch 3/50\n",
      "1104/1104 [==============================] - 1s 711us/sample - loss: 0.7740 - auc: 0.5129 - val_loss: 0.6684 - val_auc: 0.4778\n",
      "Epoch 4/50\n",
      "1104/1104 [==============================] - 1s 764us/sample - loss: 0.7067 - auc: 0.4821 - val_loss: 0.6581 - val_auc: 0.3171\n",
      "Epoch 5/50\n",
      "1104/1104 [==============================] - 1s 716us/sample - loss: 0.6671 - auc: 0.4975 - val_loss: 0.6169 - val_auc: 0.2777\n",
      "Epoch 6/50\n",
      "1104/1104 [==============================] - 1s 765us/sample - loss: 0.6672 - auc: 0.4989 - val_loss: 0.6042 - val_auc: 0.3402\n",
      "Epoch 7/50\n",
      "1104/1104 [==============================] - 1s 726us/sample - loss: 0.6385 - auc: 0.4944 - val_loss: 0.6024 - val_auc: 0.2026\n",
      "Epoch 8/50\n",
      "1104/1104 [==============================] - 1s 724us/sample - loss: 0.6162 - auc: 0.5245 - val_loss: 0.6198 - val_auc: 0.2872\n",
      "Epoch 9/50\n",
      "1104/1104 [==============================] - 1s 753us/sample - loss: 0.6024 - auc: 0.5760 - val_loss: 0.6317 - val_auc: 0.2355\n",
      "Epoch 10/50\n",
      "1088/1104 [============================>.] - ETA: 0s - loss: 0.6160 - auc: 0.5413\n",
      "Epoch 00010: ReduceLROnPlateau reducing learning rate to 1.0000000474974514e-05.\n",
      "1104/1104 [==============================] - 1s 826us/sample - loss: 0.6162 - auc: 0.5404 - val_loss: 0.6174 - val_auc: 0.2110\n",
      "Epoch 11/50\n",
      "1104/1104 [==============================] - 1s 779us/sample - loss: 0.6066 - auc: 0.5711 - val_loss: 0.6146 - val_auc: 0.1922\n",
      "Epoch 12/50\n",
      "1104/1104 [==============================] - 1s 940us/sample - loss: 0.5919 - auc: 0.5860 - val_loss: 0.6117 - val_auc: 0.1659\n"
     ]
    }
   ],
   "source": [
    "\"\"\"模型训练\"\"\"\n",
    "callbacks = [\n",
    "    EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True),   # 早停\n",
    "    ReduceLROnPlateau(monitor='val_loss', patience=3, factor=0.01, verbose=1)  # 调整学习率\n",
    "]\n",
    "history = model.fit(X_train, \n",
    "                    y_train, \n",
    "                    epochs=epochs, \n",
    "                    validation_split=0.2, \n",
    "                    batch_size=batch_size,\n",
    "                    callbacks = callbacks\n",
    "                   )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "thirty-tennis",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-06T14:27:58.560532Z",
     "start_time": "2021-03-06T14:27:58.425604Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\"\"\"可视化下看看训练情况\"\"\"\n",
    "plt.plot(history.history['loss'])\n",
    "plt.plot(history.history['val_loss'])\n",
    "plt.title('Model loss')\n",
    "plt.ylabel('Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.legend(['Train', 'Test'], loc='upper left')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.9"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
